﻿#include "OiPCH.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        class SRPlcTaskImpl : public Impl<SRPlcTask>
        {
        public:
            void init();

        public:
            QString _formular;
            QString _preset;
            double _width;
            double _circum;
            ushort _startSignal;

            QList<PLScannerOutput> _wDatas;

            QMutex _mutex;
            bool _quit;

        public:
            bool _simulation;
            bool _plcEnable;
            QString _plcSys;
            QString _deviceName;
            QString _addr;
            QString _port;
            QString _sysPath;
            int _heartbeat;
        };

        void SRPlcTaskImpl::init()
        {
            OI_Q(SRPlcTask);

            _quit = false;

            _heartbeat = 0;
        }


        SRPlcTask::SRPlcTask(QObject* parent) 
            : QThread(parent)
        {
            OI_I(SRPlcTask)->init();
            
            init();
        }

        SRPlcTask::~SRPlcTask()
        {
            OI_E(SRPlcTask);

            exit();
        }

        void SRPlcTask::init()
        {
            OI_F(SRPlcTask);
            auto settings = Core::OiSettings();

            d->_simulation = PLScanner::instance()->simulation;
            d->_plcEnable = settings->value("PLC/Enable", false).toBool();

            d->_deviceName = Core::OiSettings()->value("PLC/DeviceName", "PLC_SR_TG").toString();
            d->_plcSys = Core::OiSettings()->value("PLC/Sys", "ControlLogix").toString();
            d->_addr = Core::OiSettings()->value("PLC/Address", "192.168.1.1").toString();
            d->_port = Core::OiSettings()->value("PLC/Port", "44818").toString();
            d->_sysPath = Core::OiSettings()->value("PLC/Path", "1,0").toString();

            if(!d->_simulation && d->_plcEnable)
            {
                start();
            }
        }

        void SRPlcTask::exit()
        {
            if(isRunning())
            {
                quit();
                wait();
            }
        }

        void SRPlcTask::run()
        {
            OI_F(SRPlcTask);

            bool quit = false;
            int action;
            int heartbeat = 0;
            PLScannerOutput output;

            if(d->_simulation || !d->_plcEnable)
            {
                return;
            }

            QSharedPointer<Util::PLC> device = Util::PLCService::instance()->get(qPrintable(d->_deviceName));

            if(device.isNull())
            {
                std::string path;
                path += "protocol=ab-eip";
                path += "&cpu=" + d->_plcSys.toStdString();
                path += "&gateway=" + d->_addr.toStdString();
                path += "&gateway_port=" + d->_port.toStdString();
                path += "&path=" + d->_sysPath.toStdString();

                device = Util::PLC::create(qPrintable(d->_deviceName), path.c_str());
                Util::PLCService::instance()->add(qPrintable(d->_deviceName), device);
            }

            if(!device->isValid() || device->connect() != 0)
            {
                PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_INVALID);
                return;
            }

            while(!quit)
            {
                d->_mutex.lock();
                quit = d->_quit;
                action = d->_wDatas.size();
                heartbeat = d->_heartbeat;
                if(action != 0)
                {
                    output = d->_wDatas.front();
                    d->_wDatas.pop_front();
                }

                d->_mutex.unlock();

                if(action != 0)
                {
                    std::vector<float> resData(1);
                    resData[0] = output.result;

                    device->appendTag("g_GTI_Read[14]", 4, 1);

                    if(device->set("g_GTI_Read[14]", resData.data(), 0, 1) == 0)
                    {
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                    }

                    std::vector<float> valueData(13);
                    memcpy(valueData.data(), (char*)&output + sizeof(output.result), 13 * sizeof(float));

                    device->appendTag("g_GTI_Read", 4, 13);

                    if(device->set("g_GTI_Read", valueData.data(), 0, 13) == 0)
                    {
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                    }

                    device->push();
                    //mark
                    QThread::msleep(10);
                }
                else
                {
                    QString formular;
                    QString preset;
                    double width = 0.0;
                    double circum = 0.0;
                    ushort startSignal = 0;

                    device->appendTag("g_GTI_Read[13]", 4, 1);

                    std::vector<float> heartData(1);
                    heartData[0] = heartbeat;

                    if(device->set("g_GTI_Read[13]", heartData.data(), 0, 1) == 0)
                    {
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                        OiWarning() << "g_GTI_Read[13] error";
                    }

                    device->push("g_GTI_Read[13]");

                    device->appendTag("g_GTI_Write1", 88, 1);//device->appendTag("g_GTI_Write1", 0, 88);
                    device->appendTag("g_GTI_Write2", 88, 1);//device->appendTag("g_GTI_Write2", 0, 88);
                    device->appendTag("g_GTI_Write3", 4, 1);
                    device->appendTag("g_GTI_Write4", 4, 1);
                    device->appendTag("g_GTI_Write5", 2, 1);
                    device->pull();

//                     std::vector<uint8_t> formularData(20);
//                     if(device->get("g_GTI_Write1", formularData.data(), 0, 20) == 0)
//                     {
//                         formular = QString::fromStdString(reinterpret_cast<char*>(formularData.data()));
//                         OiWarning() << formularData;
//                     }
//                     else
//                     {
//                         PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
//                     }
// 
//                     std::vector<uint8_t> presetData(20);
//                     if(device->get("g_GTI_Write2", presetData.data(), 0, 20) == 0)
//                     {
//                         preset = QString::fromStdString(reinterpret_cast<char*>(presetData.data()));
//                         OiWarning() << presetData;
//                     }
//                     else
//                     {
//                         PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
//                     }
                    
                    std::string formularData(88, 0);
                    if(device->get("g_GTI_Write1", formularData, 0, 88) == 0)
                    {
                        formular = QString::fromStdString(formularData);
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                        OiWarning() << "g_GTI_Write1 error";
                    }

                    std::string presetData(88, 0);
                    if(device->get("g_GTI_Write2", presetData, 0, 88) == 0)
                    {
                        preset = QString::fromStdString(presetData);
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                        OiWarning() << "g_GTI_Write2 error";
                    }

                    std::vector<float> wData(1);
                    if(device->get("g_GTI_Write3", wData.data(), 0, 1) == 0)
                    {
                        width = wData[0];
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                        OiWarning() << "g_GTI_Write3 error";
                    }

                    std::vector<float> cData(1);
                    if(device->get("g_GTI_Write4", cData.data(), 0, 1) == 0)
                    {
                        circum = cData[0];
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                        OiWarning() << "g_GTI_Write4 error";
                    }

                    std::vector<uint16_t> ssData(1);
                    if(device->get("g_GTI_Write5", ssData.data(), 0, 1) == 0)
                    {
                        startSignal = ssData[0];
                    }
                    else
                    {
                        PLScannerError::instance()->reportError(PLScannerError::ERR_PLC_TIMEOUT);
                        OiWarning() << "g_GTI_Write5 error";
                    }

                    

                    d->_mutex.lock();
                    d->_formular = formular;
                    d->_preset = preset;
                    d->_width = width;
                    d->_circum = circum;
                    d->_startSignal = startSignal;
                    d->_mutex.unlock();

                    QThread::msleep(10);
                }
            }

            Util::PLCService::instance()->exit();
        }

        void SRPlcTask::quit()
        {
            OI_F(SRPlcTask);
            QMutexLocker lock(&d->_mutex);
            d->_quit = true;
        }

        void SRPlcTask::start()
        {
            OI_F(SRPlcTask);
            d->_mutex.lock();
            d->_quit = false;
            d->_wDatas.clear();
            d->_mutex.unlock();

            QThread::start();
        }

        bool SRPlcTask::write(const PLScannerOutput& ouput)
        {
            OI_F(SRPlcTask);

            if(d->_simulation || !d->_plcEnable)
            {
                return true;
            }

            if(!isRunning())
            {
                start();
            }

            QMutexLocker lock(&d->_mutex);
            d->_wDatas.push_back(ouput);

            return true;
        }

        bool SRPlcTask::readParams(ProductParams& params)
        {
            OI_F(SRPlcTask);

            QString formular;
            QString preset;
            double width = 0.0;
            double circum = 0.0;

            d->_mutex.lock();
            formular = d->_formular;
            preset = d->_preset;
            width = d->_width;
            circum = d->_circum;
            d->_mutex.unlock();

            formular.remove(QChar(0));
            preset.remove(QChar(0));
            formular = formular.simplified();
            preset = preset.simplified();

            if(width == 0.0 || circum == 0.0 || formular.isEmpty() || preset.isEmpty())
            {
                OiWarning() << "参数读取错误，" << "Width:" << width << "circum:" << circum << " preset:" << preset << " formular:" << formular;
                return false;
            }

            params = ProductParams(width, circum / (2 * M_PI), formular, preset);
            return true;
        }

        bool SRPlcTask::readStatus(ProductStatus& status)
        {
            OI_F(SRPlcTask);

            double startSignal = 0;

            d->_mutex.lock();
            startSignal = d->_startSignal;
            d->_mutex.unlock();

            status.enable = ( startSignal == 1 );
            return true;
        }

        bool SRPlcTask::heartbeat(int heartbeat)
        {
            OI_F(SRPlcTask);

            if(d->_simulation || !d->_plcEnable)
            {
                return true;
            }

            if(!isRunning())
            {
                start();
            }

            QMutexLocker lock(&d->_mutex);
            d->_heartbeat = heartbeat;

            return true;
        }

        SRPlcTask* SRPlcTask::instance()
        {
            static SRPlcTask s_plc;
            return &s_plc;
        }

    }
}